package org.dce.screencapturer.actions.ui;

import java.awt.AWTEvent;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JFrame;

import org.dce.screencapturer.Common;

@SuppressWarnings("serial")
public class AreaSelector extends JFrame {

	private static final Stroke DASHED_STROKE = new BasicStroke(
			0.2f, 
			BasicStroke.CAP_SQUARE, 
			BasicStroke.JOIN_ROUND,
			1.0f,
			new float[]{10f,10f},
			0f);

	private List<AreaSelectorListener> listeners = new ArrayList<AreaSelectorListener>();
	private BufferedImage image;
	private AreaEditor areaEditor = new AreaEditor();

	public AreaSelector(BufferedImage image){
		this.image = image;
		setLocation(0, 0);
		setSize(getToolkit().getScreenSize());
		setUndecorated(true);
		setContentPane(areaEditor);
	}
	
	public void addListener(AreaSelectorListener listener){
		listeners.add(listener);
	}
	
	public void removeListener(AreaSelectorListener listener){
		listeners.remove(listener);
	}
	
	protected void notifyAreaSelected(BufferedImage image){
		AreaSelectorEvent event = new AreaSelectorEvent(this, image);
		
		for (AreaSelectorListener listener:new ArrayList<AreaSelectorListener>(listeners)){
			listener.areaSelected(event);
		}
	}
	
	protected void notifySelectionCanceled(){
		AreaSelectorEvent event = new AreaSelectorEvent(this);
		
		for (AreaSelectorListener listener:listeners){
			listener.selectionCanceled(event);
		}
	}
	
	private class AreaEditor extends JComponent {

		private Point firstPoint;
		private Point currentPoint;
		
		public AreaEditor(){
			setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
			enableEvents(AWTEvent.MOUSE_EVENT_MASK|AWTEvent.MOUSE_MOTION_EVENT_MASK);
		}
		
		protected void paintComponent(Graphics g){
			Graphics2D g2d = (Graphics2D)g;

			g2d.setFont(new Font(Font.DIALOG,Font.BOLD,18));
			
			g2d.drawImage(image,0,0,this);

			drawLegend(g, g2d);

			if (firstPoint != null && currentPoint != null){
				int x = Math.min(currentPoint.x, firstPoint.x);
				int y = Math.min(currentPoint.y, firstPoint.y);
				
				int width = Math.abs(currentPoint.x - firstPoint.x);
				int height = Math.abs(currentPoint.y - firstPoint.y);

				g2d.setStroke(DASHED_STROKE);
				g2d.setXORMode(Color.WHITE);
				g2d.drawRect(x,y, width, height);
			}
		}

		private void drawLegend(Graphics g, Graphics2D g2d) {
			String legend = Common.BUNDLE.getString("areaSelector.legend");
			
			FontMetrics metrics = g2d.getFontMetrics();
			
			int maxWidth = metrics.stringWidth(legend);
			int width = getWidth();
			
			int legendX = (width - maxWidth - 50) / 2;
			
			int maxHeight = metrics.getHeight() + 50;

			Composite composite = g2d.getComposite();
			g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f));
			g2d.setColor(Color.YELLOW);
			
			g2d.fillRoundRect(legendX, 10, maxWidth+50, maxHeight, 10, 10);
			g2d.setComposite(composite);
			
			g2d.setColor(Color.BLACK);
			
			g.drawString(legend, (width - maxWidth) / 2, 45 - metrics.getHeight() / 2);
			g2d.drawRoundRect(legendX, 10, maxWidth+50, maxHeight, 10, 10);
		}
		
		protected void processMouseEvent(MouseEvent event){
			if (event.getID() == MouseEvent.MOUSE_PRESSED 
				&& event.getButton() == MouseEvent.BUTTON1){
			
				if (firstPoint == null){
					firstPoint = event.getPoint();
				} else {
				}
			} else if (event.getID() == MouseEvent.MOUSE_RELEASED
				&& event.getButton() == MouseEvent.BUTTON1
				&& firstPoint != null){

				doSelection(event.getPoint());
			}
			super.processMouseEvent(event);
		}
		
		protected void processMouseMotionEvent(MouseEvent event){
			if (event.getID() == MouseEvent.MOUSE_DRAGGED){
				currentPoint = event.getPoint();
				repaint();
			}
		}
		
		private void doSelection(Point lastPoint){
			
			int x = Math.min(lastPoint.x, firstPoint.x);
			int y = Math.min(lastPoint.y, firstPoint.y);
			
			int width = Math.abs(lastPoint.x - firstPoint.x);
			int height = Math.abs(lastPoint.y - firstPoint.y);
			
			if (width > 5 && height > 5){
				BufferedImage selection = image.getSubimage(x,y,width,height);
				notifyAreaSelected(selection);
			} else {
				notifySelectionCanceled();
			}
		}
	}
}
